package com.taoes.simpledocker.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.InspectImageResponse;
import com.github.dockerjava.api.model.Image;
import com.github.dockerjava.api.model.SearchItem;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import com.taoes.simpledocker.config.DockerClientFactory;
import com.taoes.simpledocker.controller.image.ImageTagUpdateRequest;
import com.taoes.simpledocker.controller.image.PushImageRequest;
import com.taoes.simpledocker.model.ResponseModel;
import com.taoes.simpledocker.model.Role;
import com.taoes.simpledocker.service.ImageService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * @author lfs
 * @desciption:镜像管理服务
 * @date 2021/12/4 11:35 下午
 */
@Api(tags = "Docker镜像管理")
@ApiSort(value = 3)
@Slf4j
@RestController
@RequestMapping("/api/images")
@RequiredArgsConstructor
public class ImageController {

    private final DockerClientFactory clientFactory;

    private final ImageService imageService;

    @ApiOperation("查看镜像列表")
    @SaCheckPermission(value = "image:query", orRole = Role.ADMIN_ROLE_NAME)
    @GetMapping("/list")
    public ResponseModel<List<Image>> list(@RequestParam(required = false) String searchKey) {
        final DockerClient dockerClient = clientFactory.get();
        final List<Image> images = dockerClient.listImagesCmd().withShowAll(true).exec();
        if (StrUtil.isBlank(searchKey)) {
            return ResponseModel.ok(images);
        }

        final List<Image> result = new ArrayList<>();
        for (Image image : images) {
            final String[] tags = image.getRepoTags();
            if (tags == null || tags.length == 0) {
                continue;
            }
            for (String tag : tags) {
                if (tag != null && tag.contains(searchKey)) {
                    result.add(image);
                }
            }
        }

        return ResponseModel.ok(result);
    }

    @ApiOperation("查看镜像详情")
    @SaCheckPermission(value = "image:query", orRole = Role.ADMIN_ROLE_NAME)
    @GetMapping("/{imageId}/inspect")
    public InspectImageResponse findById(@PathVariable String imageId) {
        return imageService.inspect(imageId);
    }

    @ApiOperation("拉取镜像")
    @SaCheckPermission(value = "image:pull", orRole = Role.ADMIN_ROLE_NAME)
    @PutMapping
    public Object pull(@RequestParam String imageTag) {
        final DockerClient dockerClient = clientFactory.get();
        return dockerClient.pullImageCmd(imageTag).exec(null);
    }

    @ApiOperation("搜索镜像")
    @SaCheckPermission(value = "image:search", orRole = Role.ADMIN_ROLE_NAME)
    @PostMapping("/search")
    public ResponseModel<List<SearchItem>> searchImage(String term) {
        final DockerClient client = clientFactory.get();
        final List<SearchItem> exec = client.searchImagesCmd(term).withLimit(10).exec();
        return ResponseModel.ok(exec);
    }

    @ApiOperation(value = "推送镜像", notes = "将镜像推送到配置的docker仓库里面")
    @SaCheckPermission(value = "image:push", orRole = Role.ADMIN_ROLE_NAME)
    @PostMapping
    public Object push(@RequestBody PushImageRequest request) {
        final DockerClient dockerClient = clientFactory.get();
        return dockerClient.pushImageCmd(request.getImageId()).exec(null);
    }

    @ApiOperation("移除镜像")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "imageId", value = "镜像id或者镜像tag都适用，此处建议使用镜像tag进行删除",required = true),
            @ApiImplicitParam(name = "force", value = "强制删除,默认为false",required = false)})
    @SaCheckPermission(value = "image:delete", orRole = Role.ADMIN_ROLE_NAME)
    @DeleteMapping("/{imageId}")
    public ResponseModel<String> remove(@PathVariable String imageId, @RequestParam(defaultValue = "false") Boolean force) {
        imageService.remove(imageId, force);
        return ResponseModel.ok("OK");
    }

    @ApiOperation(value = "清理镜像", notes = "清除所有没有使用的镜像")
    @SaCheckPermission(value = "image:prune", orRole = Role.ADMIN_ROLE_NAME)
    @PostMapping("/prune")
    public ResponseModel<Boolean> purge() {
        imageService.pruneImage();
        return ResponseModel.ok(Boolean.TRUE);
    }

    /**
     * 更新镜像信息
     */
    @ApiOperation("标记镜像")
    @SaCheckPermission(value = "image:tag", orRole = Role.ADMIN_ROLE_NAME)
    @PostMapping("/update/tag")
    public ResponseModel<Boolean> updateImageInfo(@RequestBody ImageTagUpdateRequest req) {
        req.check();
        imageService.tag(req.getImageId(), req.getNewTag());
        return ResponseModel.ok(Boolean.TRUE);
    }

    /**
     * 保存镜像
     */
    @ApiOperation("导出镜像")
    @ApiImplicitParams(
            @ApiImplicitParam(name = "nameTag", value = "镜像id或者镜像tag都适用",required = true))
    @SaCheckPermission(value = "image:export", orRole = Role.ADMIN_ROLE_NAME)
    @GetMapping("/save/{nameTag}")
    public void save(@PathVariable String nameTag, HttpServletResponse response) {
        try (InputStream input = imageService.save(nameTag);
             ServletOutputStream output = response.getOutputStream()) {
            String currentTime = DateUtil.format(DateUtil.date(), "yyyyMMdd_HHmmss_");
            response.setContentType("application/x-tar;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + currentTime + nameTag + ".tar.gz");
            // 循环取出流中的数据
            byte[] b = new byte[1024];
            int len;
            while ((len = input.read(b)) > 0) {
                output.write(b, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 批量保存镜像
     */
    @ApiOperation("批量导出镜像")
    @SaCheckPermission(value = "image:export", orRole = Role.ADMIN_ROLE_NAME)
    @PostMapping("/saveBatch")
    public void saveBatch(@RequestBody List<String> nameTagList, HttpServletRequest request, HttpServletResponse response) {
        imageService.saveBatch(nameTagList, request, response);
    }

    @ApiOperation(value = "导入镜像",notes = "导入的镜像文件，一般为tar包")
    @SaCheckPermission(value = "image:import", orRole = Role.ADMIN_ROLE_NAME)
    @PostMapping("/load")
    public ResponseModel<Boolean> importImage(MultipartFile file) {
        DockerClient dockerClient = clientFactory.get();
        String originalFilename = file.getOriginalFilename();
        try (InputStream in = file.getInputStream()) {
            log.info("Start LoadImage ====>" + originalFilename);
            dockerClient.loadImageCmd(in).exec();
            log.info("导入成功");
            return ResponseModel.ok(true);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}
